<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
        <slot name="title"></slot>
        <Icon icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" />
      </div>
      <CollapseTransition>
        <ul :class="prefixCls" v-show="opened">
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>

    <Popover
      placement="rightTop"
      :overlay-class-name="`${prefixCls}-menu-popover`"
      v-else
      :open="getIsOpened"
      @open-change="handleVisibleChange"
      :overlay-style="getOverlayStyle"
      :align="{ offset: [0, 0] }"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle
            }
          ]"
        >
          <slot name="title"></slot>
        </div>
        <Icon
          v-if="getParentSubMenu"
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="opened">
        <div v-bind="getEvents(true)">
          <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
            <slot></slot>
          </ul>
        </div>
      </template>
    </Popover>
  </li>
</template>

<script lang="ts">
  import type { CSSProperties, PropType } from "vue";
  import {
    computed,
    defineComponent,
    getCurrentInstance,
    inject,
    onBeforeMount,
    provide,
    reactive,
    toRefs,
    unref
  } from "vue";
  import type { SubMenuProvider } from "./Types";
  import { useDesign } from "@mfish/core/hooks";
  import { propTypes } from "@mfish/core/utils/PropTypes";
  import { useMenuItem } from "./UseMenu";
  import { useSimpleRootMenuContext } from "./UseSimpleMenuContext";
  import { CollapseTransition } from "@mfish/core/components/Transition";
  import { Icon } from "@mfish/core/components/Icon";
  import { Popover } from "ant-design-vue";
  import { isBoolean, isObject } from "@mfish/core/utils/Is";
  import { mitt } from "@mfish/core/utils/Mitt";
  import { Recordable, TimeoutHandle } from "@mfish/types";

  const DELAY = 200;
  export default defineComponent({
    name: "SubMenu",
    components: {
      Icon,
      CollapseTransition,
      Popover
    },
    props: {
      name: {
        type: [String, Number] as PropType<string | number>,
        required: true
      },
      disabled: propTypes.bool,
      collapsedShowTitle: propTypes.bool
    },
    setup(props) {
      const instance = getCurrentInstance();

      const state = reactive({
        active: false,
        opened: false
      });

      const data = reactive({
        timeout: null as TimeoutHandle | null,
        mouseInChild: false,
        isChild: false
      });

      const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance);

      const { prefixCls } = useDesign("menu");

      const subMenuEmitter = mitt();

      const { rootMenuEmitter } = useSimpleRootMenuContext();

      const {
        addSubMenu: parentAddSubmenu,
        removeSubMenu: parentRemoveSubmenu,
        removeAll: parentRemoveAll,
        getOpenNames: parentGetOpenNames,
        isRemoveAllPopup,
        sliceIndex,
        level,
        props: rootProps,
        handleMouseleave: parentHandleMouseleave
      } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!;

      const getClass = computed(() => {
        return [
          `${prefixCls}-submenu`,
          {
            [`${prefixCls}-item-active`]: state.active,
            [`${prefixCls}-opened`]: state.opened,
            [`${prefixCls}-submenu-disabled`]: props.disabled,
            [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
            [`${prefixCls}-child-item-active`]: state.active
          }
        ];
      });

      const getAccordion = computed(() => rootProps.accordion);
      const getCollapse = computed(() => rootProps.collapse);
      const getTheme = computed(() => rootProps.theme);

      const getOverlayStyle = computed((): CSSProperties => {
        return {
          minWidth: "200px"
        };
      });

      const getIsOpened = computed(() => {
        const name = props.name;
        if (unref(getCollapse)) {
          return parentGetOpenNames().includes(name);
        }
        return state.opened;
      });

      const getSubClass = computed(() => {
        const isActive = rootProps.activeSubMenuNames.includes(props.name);
        return [
          `${prefixCls}-submenu-title`,
          {
            [`${prefixCls}-submenu-active`]: isActive,
            [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
            [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0
          }
        ];
      });

      function getEvents(deep: boolean) {
        if (!unref(getCollapse)) {
          return {};
        }
        return {
          onMouseenter: handleMouseenter,
          onMouseleave: () => handleMouseleave(deep)
        };
      }

      function handleClick() {
        const { disabled } = props;
        if (disabled || unref(getCollapse)) return;
        const opened = state.opened;

        if (unref(getAccordion)) {
          const { uidList } = getParentList();
          rootMenuEmitter.emit("on-update-opened", {
            opend: false,
            parent: instance?.parent,
            uidList
          });
        } else {
          rootMenuEmitter.emit("open-name-change", {
            name: props.name,
            opened: !opened
          });
        }
        state.opened = !opened;
      }

      function handleMouseenter() {
        const disabled = props.disabled;
        if (disabled) return;

        subMenuEmitter.emit("submenu:mouse-enter-child");

        const index = parentGetOpenNames().indexOf(props.name);

        sliceIndex(index);

        const isRoot = level === 0 && parentGetOpenNames().length === 2;
        if (isRoot) {
          parentRemoveAll();
        }
        data.isChild = parentGetOpenNames().includes(props.name);
        clearTimeout(data.timeout!);
        data.timeout = setTimeout(() => {
          parentAddSubmenu(props.name);
        }, DELAY);
      }

      function handleMouseleave(deepDispatch = false) {
        const parentName = getParentMenu.value?.props.name;
        if (!parentName) {
          isRemoveAllPopup.value = true;
        }

        if (parentGetOpenNames().slice(-1)[0] === props.name) {
          data.isChild = false;
        }

        subMenuEmitter.emit("submenu:mouse-leave-child");
        if (data.timeout) {
          clearTimeout(data.timeout!);
          data.timeout = setTimeout(() => {
            if (isRemoveAllPopup.value) {
              parentRemoveAll();
            } else if (!data.mouseInChild) {
              parentRemoveSubmenu(props.name);
            }
          }, DELAY);
        }
        if (deepDispatch && getParentSubMenu.value) {
          parentHandleMouseleave?.(true);
        }
      }

      onBeforeMount(() => {
        subMenuEmitter.on("submenu:mouse-enter-child", () => {
          data.mouseInChild = true;
          isRemoveAllPopup.value = false;
          clearTimeout(data.timeout!);
        });
        subMenuEmitter.on("submenu:mouse-leave-child", () => {
          if (data.isChild) return;
          data.mouseInChild = false;
          clearTimeout(data.timeout!);
        });

        rootMenuEmitter.on("on-update-opened", (data: boolean | (string | number)[] | Recordable) => {
          if (unref(getCollapse)) return;
          if (isBoolean(data)) {
            state.opened = data;
            return;
          }
          if (isObject(data) && rootProps.accordion) {
            const { opened, parent, uidList } = data as Recordable;
            if (parent === instance?.parent) {
              state.opened = opened;
            } else if (!uidList.includes(instance?.uid)) {
              state.opened = false;
            }
            return;
          }

          if (props.name && Array.isArray(data)) {
            state.opened = (data as (string | number)[]).includes(props.name);
          }
        });

        rootMenuEmitter.on("on-update-active-name:submenu", (data: number[]) => {
          if (instance?.uid) {
            state.active = data.includes(instance?.uid);
          }
        });
      });

      function handleVisibleChange(visible: boolean) {
        state.opened = visible;
      }

      // provide
      provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
        addSubMenu: parentAddSubmenu,
        removeSubMenu: parentRemoveSubmenu,
        getOpenNames: parentGetOpenNames,
        removeAll: parentRemoveAll,
        isRemoveAllPopup,
        sliceIndex,
        level: level + 1,
        handleMouseleave,
        props: rootProps
      });

      return {
        getClass,
        prefixCls,
        getCollapse,
        getItemStyle,
        handleClick,
        handleVisibleChange,
        getParentSubMenu,
        getOverlayStyle,
        getTheme,
        getIsOpened,
        getEvents,
        getSubClass,
        ...toRefs(state),
        ...toRefs(data)
      };
    }
  });
</script>
